package chainmaker.sdk.demo;

import com.google.protobuf.ByteString;
import org.chainmaker.pb.common.ContractOuterClass;
import org.chainmaker.pb.common.Request;
import org.chainmaker.pb.common.ResultOuterClass;
import org.chainmaker.sdk.ChainClient;
import org.chainmaker.sdk.ChainClientException;
import org.chainmaker.sdk.User;
import org.chainmaker.sdk.crypto.ChainMakerCryptoSuiteException;
import org.chainmaker.sdk.utils.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.FunctionReturnDecoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.StaticArray;
import org.web3j.abi.datatypes.Utf8String;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.abi.datatypes.generated.Uint8;

import java.util.*;

import static chainmaker.sdk.demo.InitClient.adminUser1;
import static chainmaker.sdk.demo.InitClient.chainClient;


public class Contract {

    private static final String QUERY_CONTRACT_METHOD = "query";
    private static final String INVOKE_CONTRACT_METHOD = "appendAuditProjectTags";
    private static final String CONTRACT_NAME = "ProjectCertificate5";
    private static final String CONTRACT_FILE_PATH = "rust-fact-1.0.0.wasm";

    public static void createContract(ChainClient chainClient, User adminUser1) {
        ResultOuterClass.TxResponse responseInfo = null;
        try {
            byte[] byteCode = FileUtils.getResourceFileBytes(CONTRACT_FILE_PATH);

            // 1. create payload
            Request.Payload payload = chainClient.createContractCreatePayload(CONTRACT_NAME, "1", byteCode,
                    ContractOuterClass.RuntimeType.WASMER, null);
            //2. create payloads with endorsement
            Request.EndorsementEntry[] endorsementEntries = SdkUtils
                    .getEndorsers(payload, new User[]{adminUser1});

            // 3. send request
            responseInfo = chainClient.sendContractManageRequest(payload, endorsementEntries, 10000, 10000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(responseInfo);
    }

    public static void invokeContract(ChainClient chainClient) {
        ResultOuterClass.TxResponse responseInfo = null;
        try {
            Map<String, byte[]> params = new HashMap<>();
            Function function = new Function( "getTransaction" ,
                    Arrays.asList(
                            new Utf8String("key"),
                            new Utf8String("txId")),
                    Arrays.asList(new TypeReference<Utf8String>(){},
                            new TypeReference<Uint256>(){},
                            new TypeReference<Uint8>(){},
                            new TypeReference<Uint256>(){},
                            new TypeReference<Uint256>(){},
                            new TypeReference<Uint256>(){},
                            new TypeReference<Utf8String>(){},
                            new TypeReference<Utf8String>(){},
                            new TypeReference<Utf8String>(){}
                    ));

            String methodDataStr = FunctionEncoder.encode(function);
            String method = methodDataStr.substring(0,10);
            System.out.println(method);
            params.put("data", methodDataStr.getBytes());

            responseInfo = chainClient.invokeContract(Utils.calcContractName("PointsTransaction4"), method,
                    null, params,10000, 10000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(responseInfo);
    }

    public static void queryContract(ChainClient chainClient) throws ChainClientException, ChainMakerCryptoSuiteException {

        Map<String, byte[]> params = new HashMap<>();

        Function function = new Function( "getinfo" ,
                Collections.emptyList(),
                Arrays.asList(
                        new TypeReference<Uint256>(){},
                        new TypeReference<Uint256>(){}
                ));
        String methodDataStr = FunctionEncoder.encode(function);
        String method = methodDataStr.substring(0,10);
        params.put("data", methodDataStr.getBytes());

        ResultOuterClass.TxResponse responseInfo = InitClient.chainClient.queryContract(Utils.calcContractName("PointsTransaction4"), method, null, params, 10000);
        String resData = responseInfo.toString();
        System.out.println(resData);

        System.out.println("Decoded string: " + responseInfo.getContractResult().getResult());

        ByteString result = responseInfo.getContractResult().getResult();
        System.out.println(FunctionReturnDecoder.decode(resData,function.getOutputParameters()));
//        return decodedString;
    }
}
